//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics.Contracts;
using System.IO;
using System.Text;
using System.Xml;

namespace OpenLS.Vml.Parsing
{
    internal class SgmlReader : XmlReader
    {
        private const string aterm = " \t\r\n='\"/>";
        private const string avterm = " \t\r\n>";
        private const string cdataterm = "\t\r\n[<>";
        private const string declterm = " \t\r\n><";
        private const string dtterm = " \t\r\n>";
        private const CaseFolding folding = CaseFolding.None;
        private const string piTerm = " \t\r\n?";
        private const string startTag = null;
        private static readonly string _tagterm = " \t\r\n=/><";
        private readonly XmlNameTable _nametable = new NameTable();
        private Attribute _attribute;
        private int _attributePosition;
        private Entity _current;
        private object _endTag;
        private bool _foundRoot;
        private StringBuilder _name;
        private Node _newnode;
        private char _partial;
        private int _poptodepth;
        private int _rootCount;
        private StringBuilder _sb;
        private HWStack<Node> _stack;
        private SgmlReaderState _state;


        private readonly TextReader _inputReader;

        public SgmlReader(TextReader reader)
        {
            _inputReader = reader;
            initialize(); 
        }


        public override XmlNodeType NodeType
        {
            get
            {
                if (_state == SgmlReaderState.Attribute)
                {
                    return XmlNodeType.Attribute;
                }
                if (_state == SgmlReaderState.AttributeValue)
                {
                    return XmlNodeType.Text;
                }
                if (_state == SgmlReaderState.EndTag || _state == SgmlReaderState.AutoCloseTag)
                {
                    return XmlNodeType.EndElement;
                }
                return Node._nodeType;
            }
        }

        /// <summary>
        /// Gets the qualified name of the current node.
        /// </summary>
        /// <returns>
        /// The qualified name of the current node. For example, Name is bk:book for the element &lt;bk:book&gt;.The name returned is dependent on the <see cref="P:System.Xml.XmlReader.NodeType"/> of the node. The following node types return the listed values. All other node types return an empty string.Node type Name AttributeThe name of the attribute. DocumentTypeThe document type name. ElementThe tag name. EntityReferenceThe name of the entity referenced. ProcessingInstructionThe target of the processing instruction. XmlDeclarationThe literal string xml. 
        /// </returns>
        public override string Name
        {
            get { return LocalName; }
        }

        /// <summary>
        /// Gets the local name of the current node.
        /// </summary>
        /// <returns>
        /// The name of the current node with the prefix removed. For example, LocalName is book for the element &lt;bk:book&gt;.For node types that do not have a name (like Text, Comment, and so on), this property returns String.Empty.
        /// </returns>
        public override string LocalName
        {
            get
            {
                Contract.Ensures(Contract.Result<string>() != null);
                Contract.Ensures(!Contract.Result<string>().Contains(":"));
                string result;
                if (_state == SgmlReaderState.Attribute)
                {
                    result = _attribute.LocalName;
                }
                else if (_state == SgmlReaderState.AttributeValue)
                {
                    result = null;
                }
                else
                {
                    result = Node.LocalName;
                }

                return result;
            }
        }

        /// <summary>
        /// Gets the namespace URI (as defined in the W3C Namespace specification) of the node on which the reader is positioned.
        /// </summary>
        /// <returns>
        /// The namespace URI of the current node; otherwise an empty string.
        /// </returns>
        public override string NamespaceURI
        {
            get
            {
                if (_state == SgmlReaderState.Attribute && StringUtilities.EqualsIgnoreCase(_attribute.LocalName, "xmlns"))
                {
                    return "http://www.w3.org/2000/xmlns/";
                }
                if (Node != null)
                    return Node.GetNamespaceUri(_stack.GetItems());
                return String.Empty;
            }
        }

        /// <summary>
        /// Gets the namespace prefix associated with the current node.
        /// </summary>
        /// <returns>
        /// The namespace prefix associated with the current node.
        /// </returns>
        public override string Prefix
        {
            get
            {
                Contract.Ensures(Contract.Result<string>() == null ||  !Contract.Result<string>().Contains(":"));
                switch (_state)
                {
                    case SgmlReaderState.Attribute:
                        return _attribute.Prefix;
                    case SgmlReaderState.AttributeValue:
                        return null;
                    default:
                        return Node.Prefix;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the current node can have a <see cref="P:System.Xml.XmlReader.Value"/>.
        /// </summary>
        /// <returns>
        /// true if the node on which the reader is currently positioned can have a Value; otherwise, false. If false, the node has a value of String.Empty.
        /// </returns>
        public override bool HasValue
        {
            get
            {
                if (_state == SgmlReaderState.Attribute || _state == SgmlReaderState.AttributeValue)
                {
                    return true;
                }
                return (Node.Value != null);
            }
        }

        /// <summary>
        /// Gets the text value of the current node.
        /// </summary>
        /// <returns>
        /// The value returned depends on the <see cref="P:System.Xml.XmlReader.NodeType"/> of the node. The following table lists node types that have a value to return. All other node types return String.Empty.Node type Value AttributeThe value of the attribute. CDATAThe content of the CDATA section. CommentThe content of the comment. DocumentTypeThe internal subset. ProcessingInstructionThe entire content, excluding the target. SignificantWhitespaceThe white space between markup in a mixed content model. TextThe content of the text node. WhitespaceThe white space between markup. XmlDeclarationThe content of the declaration. 
        /// </returns>
        public override string Value
        {
            get
            {
                if (_state == SgmlReaderState.Attribute || _state == SgmlReaderState.AttributeValue)
                {
                    return _attribute.Value;
                }
                return Node.Value;
            }
        }

        /// <summary>
        /// Gets the depth of the current node in the XML document.
        /// </summary>
        /// <returns>
        /// The depth of the current node in the XML document.
        /// </returns>
        public override int Depth
        {
            get
            {
                if (_state == SgmlReaderState.Attribute)
                {
                    return _stack.Count;
                }
                if (_state == SgmlReaderState.AttributeValue)
                {
                    return _stack.Count + 1;
                }
                return _stack.Count - 1;
            }
        }

        /// <summary>
        /// Gets the base URI of the current node.
        /// </summary>
        /// <returns>
        /// The base URI of the current node.
        /// </returns>
        public override string BaseURI
        {
            get { return ""; }
        }

        /// <summary>
        /// Gets a value indicating whether the current node is an empty element (for example, &lt;MyElement/&gt;).
        /// </summary>
        /// <returns>
        /// true if the current node is an element (<see cref="P:System.Xml.XmlReader.NodeType"/> equals XmlNodeType.Element) that ends with /&gt;; otherwise, false.
        /// </returns>
        public override bool IsEmptyElement
        {
            get
            {
                if (_state == SgmlReaderState.Markup || _state == SgmlReaderState.Attribute || _state == SgmlReaderState.AttributeValue)
                {
                    return Node._isEmpty;
                }
                return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the current node is an attribute that was generated from the default value defined in the DTD or schema.
        /// </summary>
        /// <returns>
        /// true if the current node is an attribute whose value was generated from the default value defined in the DTD or schema; false if the attribute value was explicitly set.
        /// </returns>
        public override bool IsDefault
        {
            get
            {
                if (_state == SgmlReaderState.Attribute || _state == SgmlReaderState.AttributeValue)
                    return _attribute.IsDefault;
                return false;
            }
        }

        /// <summary>
        /// Gets the quotation mark character used to enclose the value of an attribute node.
        /// </summary>
        /// <returns>
        /// The quotation mark character (" or ') used to enclose the value of an attribute node.
        /// </returns>
        public override char QuoteChar
        {
            get
            {
                return _attribute != null ? _attribute.QuoteChar : '\0';
            }
        }

        /// <summary>
        /// Gets the current xml:space scope.
        /// </summary>
        /// <returns>
        /// One of the <see cref="T:System.Xml.XmlSpace"/> values. If no xml:space scope exists, this property defaults to XmlSpace.None.
        /// </returns>
        public override XmlSpace XmlSpace
        {
            get
            {
                for (int i = _stack.Count - 1; i > 1; i--)
                {
                    var n = _stack[i];
                    XmlSpace xs = n._space;
                    if (xs != XmlSpace.None) return xs;
                }
                return XmlSpace.None;
            }
        }

        /// <summary>
        /// Gets the current xml:lang scope.
        /// </summary>
        /// <returns>
        /// The current xml:lang scope.
        /// </returns>
        public override string XmlLang
        {
            get
            {
                for (int i = _stack.Count - 1; i > 1; i--)
                {
                    var n = _stack[i];
                    string xmllang = n._xmlLang;
                    if (xmllang != null) return xmllang;
                }
                return String.Empty;
            }
        }

        /// <summary>
        /// Gets the number of attributes on the current node.
        /// </summary>
        /// <returns>
        /// The number of attributes on the current node.
        /// </returns>
        public override int AttributeCount
        {
            get
            {
                if (_state == SgmlReaderState.Attribute || _state == SgmlReaderState.AttributeValue)
                    return 0;
                if (Node._nodeType == XmlNodeType.Element ||
                    Node._nodeType == XmlNodeType.DocumentType)
                    return Node.AttributeCount;
                return 0;
            }
        }

        /// <summary>
        /// Gets the value of the attribute with the specified index.
        /// </summary>
        /// <returns>
        /// The value of the specified attribute.
        /// </returns>
        /// <param name="i">The index of the attribute. </param>
        public override string this[int i]
        {
            get { return GetAttribute(i); }
        }

        /// <summary>
        /// Gets the value of the attribute with the specified <see cref="P:System.Xml.XmlReader.Name"/>.
        /// </summary>
        /// <returns>
        /// The value of the specified attribute. If the attribute is not found, null is returned.
        /// </returns>
        /// <param name="name">The qualified name of the attribute. </param>
        public override string this[string name]
        {
            get { return GetAttribute(name); }
        }

        /// <summary>
        /// Gets the value of the attribute with the specified <see cref="P:System.Xml.XmlReader.LocalName"/> and <see cref="P:System.Xml.XmlReader.NamespaceURI"/>.
        /// </summary>
        /// <returns>
        /// The value of the specified attribute. If the attribute is not found, null is returned.
        /// </returns>
        /// <param name="name">The local name of the attribute. </param><param name="namespaceURI">The namespace URI of the attribute. </param>
        public override string this[string name, string namespaceUri]
        {
            get { return GetAttribute(name, namespaceUri); }
        }

        /// <summary>
        /// Gets a value indicating whether the reader is positioned at the end of the stream.
        /// </summary>
        /// <returns>
        /// true if the reader is positioned at the end of the stream; otherwise, false.
        /// </returns>
        public override bool EOF
        {
            get { return _state == SgmlReaderState.EndOfFile; }
        }

        /// <summary>
        /// Gets the state of the reader.
        /// </summary>
        /// <returns>
        /// One of the <see cref="T:System.Xml.ReadState"/> values.
        /// </returns>
        public override ReadState ReadState
        {
            get
            {
                if (_state == SgmlReaderState.Initial) return ReadState.Initial;
                if (_state == SgmlReaderState.EndOfFile) return ReadState.EndOfFile;
                return ReadState.Interactive;
            }
        }

        /// <summary>
        /// Gets the <see cref="T:System.Xml.XmlNameTable"/> associated with this implementation.
        /// </summary>
        /// <returns>
        /// The XmlNameTable enabling you to get the atomized version of a string within the node.
        /// </returns>
        public override XmlNameTable NameTable
        {
            get { return _nametable; }
        }

        private Node Node { get; set; }


        


        private void initialize()
        {
            _state = SgmlReaderState.Initial;
            _stack = new HWStack<Node>(10);
            Node = push(QualifiedName.Empty, XmlNodeType.Document, null);
            Node._isEmpty = false;
            _sb = new StringBuilder();
            _name = new StringBuilder();
            _poptodepth = 0;
            _current = null;
            _partial = '\0';
            _endTag = null;
            _attribute = null;
            _attributePosition = 0;
            _newnode = null;
            _rootCount = 0;
            _foundRoot = false;
        }

        private Node push(QualifiedName name, XmlNodeType nt, string value)
        {
            var result = _stack.Push();
            if (result == null)
            {
                result = new Node();
                _stack[_stack.Count - 1] = result;
            }
            result.Reset(name, nt, value);
            Node = result;
            return result;
        }

        private Node push(Node n)
        {
            Node n2 = push(n.Name, n._nodeType, n.Value);
            n2._isEmpty = n._isEmpty;
            n2._space = n._space;
            n2._xmlLang = n._xmlLang;
            n2._currentState = n._currentState;
            n2.CopyAttributes(n);
            Node = n2;
            return n2;
        }

        private void pop()
        {
            if (_stack.Count > 1)
            {
                Node = _stack.Pop();
            }
        }

        private Node top()
        {
            int top = _stack.Count - 1;
            if (top > 0)
            {
                return _stack[top];
            }
            return null;
        }

        /// <summary>
        /// Gets the value of the attribute with the specified <see cref="P:System.Xml.XmlReader.Name"/>.
        /// </summary>
        /// <returns>
        /// The value of the specified attribute. If the attribute is not found or the value is String.Empty, null is returned.
        /// </returns>
        /// <param name="name">The qualified name of the attribute. </param><exception cref="T:System.ArgumentNullException"><paramref name="name"/> is null.</exception>
        public override string GetAttribute(string name)
        {
            if (_state != SgmlReaderState.Attribute && _state != SgmlReaderState.AttributeValue)
            {
                int i = Node.GetAttribute(name);
                if (i >= 0) return GetAttribute(i);
            }
            return null;
        }

        /// <summary>
        /// Gets the value of the attribute with the specified <see cref="P:System.Xml.XmlReader.LocalName"/> and <see cref="P:System.Xml.XmlReader.NamespaceURI"/>.
        /// </summary>
        /// <returns>
        /// The value of the specified attribute. If the attribute is not found or the value is String.Empty, null is returned. This method does not move the reader.
        /// </returns>
        /// <param name="name">The local name of the attribute. </param><param name="namespaceURI">The namespace URI of the attribute. </param><exception cref="T:System.ArgumentNullException"><paramref name="name"/> is null.</exception>
        public override string GetAttribute(string name, string namespaceUri)
        {
            return GetAttribute(name); 
        }

        /// <summary>
        /// Gets the value of the attribute with the specified index.
        /// </summary>
        /// <returns>
        /// The value of the specified attribute. This method does not move the reader.
        /// </returns>
        /// <param name="i">The index of the attribute. The index is zero-based. (The first attribute has index 0.) </param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="i"/> is out of range. It must be non-negative and less than the size of the attribute collection.</exception>
        public override string GetAttribute(int i)
        {
            if (_state != SgmlReaderState.Attribute && _state != SgmlReaderState.AttributeValue)
            {
                Attribute a = Node.GetAttribute(i);
                if (a != null)
                    return a.Value;
            }
            throw new IndexOutOfRangeException();
        }

        /// <summary>
        /// Moves to the attribute with the specified <see cref="P:System.Xml.XmlReader.Name"/>.
        /// </summary>
        /// <returns>
        /// true if the attribute is found; otherwise, false. If false, the reader's position does not change.
        /// </returns>
        /// <param name="name">The qualified name of the attribute. </param>
        public override bool MoveToAttribute(string name)
        {
            int i = Node.GetAttribute(name);
            if (i >= 0)
            {
                MoveToAttribute(i);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Moves to the attribute with the specified <see cref="P:System.Xml.XmlReader.LocalName"/> and <see cref="P:System.Xml.XmlReader.NamespaceURI"/>.
        /// </summary>
        /// <returns>
        /// true if the attribute is found; otherwise, false. If false, the reader's position does not change.
        /// </returns>
        /// <param name="name">The local name of the attribute. </param><param name="ns">The namespace URI of the attribute. </param>
        public override bool MoveToAttribute(string name, string ns)
        {
            return MoveToAttribute(name);
        }

        /// <summary>
        /// Moves to the attribute with the specified index.
        /// </summary>
        /// <param name="i">The index of the attribute. </param>
        public override void MoveToAttribute(int i)
        {
            Attribute a = Node.GetAttribute(i);
            if (a != null)
            {
                _attributePosition = i;
                _attribute = a;
                if (_state != SgmlReaderState.Attribute)
                {
                    Node._currentState = _state; 
                }
                _state = SgmlReaderState.Attribute;
                return;
            }
            throw new IndexOutOfRangeException();
        }

        /// <summary>
        /// Moves to the first attribute.
        /// </summary>
        /// <returns>
        /// true if an attribute exists (the reader moves to the first attribute); otherwise, false (the position of the reader does not change).
        /// </returns>
        public override bool MoveToFirstAttribute()
        {
            if (Node.AttributeCount > 0)
            {
                MoveToAttribute(0);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Moves to the next attribute.
        /// </summary>
        /// <returns>
        /// true if there is a next attribute; false if there are no more attributes.
        /// </returns>
        public override bool MoveToNextAttribute()
        {
            if (_state != SgmlReaderState.Attribute && _state != SgmlReaderState.AttributeValue)
            {
                return MoveToFirstAttribute();
            }
            if (_attributePosition < Node.AttributeCount - 1)
            {
                MoveToAttribute(_attributePosition + 1);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Moves to the element that contains the current attribute node.
        /// </summary>
        /// <returns>
        /// true if the reader is positioned on an attribute (the reader moves to the element that owns the attribute); false if the reader is not positioned on an attribute (the position of the reader does not change).
        /// </returns>
        public override bool MoveToElement()
        {
            if (_state == SgmlReaderState.Attribute || _state == SgmlReaderState.AttributeValue)
            {
                _state = Node._currentState;
                _attribute = null;
                return true;
            }
            return (Node._nodeType == XmlNodeType.Element);
        }

        private void openInput()
        {
             if (_inputReader != null)
            {
                _current = new Entity(_inputReader);
            }
            else
            {
                throw new InvalidOperationException();
            }
            _current.Open(null);

        }

        /// <summary>
        /// Reads the next node from the stream.
        /// </summary>
        /// <returns>
        /// true if the next node was read successfully; false if there are no more nodes to read.
        /// </returns>
        /// <exception cref="T:System.Xml.XmlException">An error occurred while parsing the XML. </exception>
        public override bool Read()
        {
           
            if (_current == null)
            {
                openInput();
            }
            SgmlReaderState start = _state;
            if (Node._simulated)
            {
                Node._simulated = false;
                Node = top();
                _state = Node._currentState;
                return true;
            }

            bool foundnode = false;
            while (! foundnode)
            {
                switch (_state)
                {
                    case SgmlReaderState.Initial:
                        _state = SgmlReaderState.Markup;
                        _current.ReadChar();
                        goto case SgmlReaderState.Markup;
                    case SgmlReaderState.EndOfFile:
                        if (_current.Parent != null)
                        {
                            _current.Close();
                            _current = _current.Parent;
                        }
                        else
                        {
                            return false;
                        }
                        break;
                    case SgmlReaderState.EndTag:
                        if (_endTag == (object) Node.LocalName)
                        {
                            pop(); 
                            _state = SgmlReaderState.Markup;
                            goto case SgmlReaderState.Markup;
                        }
                        pop(); 
                        foundnode = true; 
                        break;
                    case SgmlReaderState.Markup:
                        if (Node._isEmpty)
                        {
                            pop();
                        }
                        Node n = Node;
                        foundnode = parseMarkup();
                        break;
                    case SgmlReaderState.PartialTag:
                        pop(); 
                        _state = SgmlReaderState.Markup;
                        foundnode = parseTag(_partial);
                        break;
                    case SgmlReaderState.PseudoStartTag:
                        foundnode = parseStartTag('<');
                        break;
                    case SgmlReaderState.AutoCloseTag:
                        pop(); 
                        if (_stack.Count <= _poptodepth)
                        {
                            _state = SgmlReaderState.Markup;
                            if (_newnode != null)
                            {
                                push(_newnode); 
                                _newnode = null;
                                _state = SgmlReaderState.Markup;
                            }
                            else if (Node._nodeType == XmlNodeType.Document)
                            {
                                _state = SgmlReaderState.EndOfFile;
                                goto case SgmlReaderState.EndOfFile;
                            }
                        }
                        foundnode = true;
                        break;
                    case SgmlReaderState.CData:
                        foundnode = parseCData();
                        break;
                    case SgmlReaderState.Attribute:
                        goto case SgmlReaderState.AttributeValue;
                    case SgmlReaderState.AttributeValue:
                        _state = SgmlReaderState.Markup;
                        goto case SgmlReaderState.Markup;
                    case SgmlReaderState.Text:
                        pop();
                        goto case SgmlReaderState.Markup;
                    case SgmlReaderState.PartialText:
                        if (parseText(_current.Lastchar, false))
                        {
                            Node._nodeType = XmlNodeType.Whitespace;
                        }
                        foundnode = true;
                        break;
                }
                if (!foundnode && _state == SgmlReaderState.EndOfFile && _stack.Count > 1)
                {
                    _poptodepth = 1;
                    _state = SgmlReaderState.AutoCloseTag;
                    Node = top();
                    return true;
                }
            }
            if (!_foundRoot && (NodeType == XmlNodeType.Element ||
                                NodeType == XmlNodeType.Text ||
                                NodeType == XmlNodeType.CDATA))
            {
                _foundRoot = true;
                return true;
            }
            return true;
        }

        private bool parseMarkup()
        {
            char ch = _current.Lastchar;
            if (ch == '<')
            {
                ch = _current.ReadChar();
                return parseTag(ch);
            }
            if (ch != Entity.Eof)
            {
                if (parseText(ch, true))
                {
                    Node._nodeType = XmlNodeType.Whitespace;
                }
                return true;
            }
            _state = SgmlReaderState.EndOfFile;
            return false;
        }

        private bool parseTag(char ch)
        {
            if (ch == '%')
            {
                return parseAspNet();
            }
            if (ch == '!')
            {
                ch = _current.ReadChar();
                if (ch == '-')
                {
                    return parseComment();
                }
                if (ch == '[')
                {
                    return parseConditionalBlock();
                }
                if (ch != '_' && !Char.IsLetter(ch))
                {
                    string value = _current.ScanToEnd(_sb, "Recovering", ">"); 
                    return false;
                }
                else
                {
                    string name = _current.ScanToken(_sb, declterm, false);
                    if (name == "DOCTYPE")
                    {
                        parseDocType();
                        if (GetAttribute("SYSTEM") == null && GetAttribute("PUBLIC") != null)
                        {
                            Node.AddAttribute(QualifiedName.Parse("SYSTEM"), "", '"', folding == CaseFolding.None);
                        }
                        return false;
                    }
                    _current.ScanToEnd(null, "Recovering", ">"); 
                    return false;
                }
            }
            switch (ch)
            {
                case '?':
                    _current.ReadChar(); 
                    return parsePI();
                case '/':
                    return parseEndTag();
                default:
                    return parseStartTag(ch);
            }
        }

        private QualifiedName scanName(string terminators)
        {
            string name = _current.ScanToken(_sb, terminators, false);
            QualifiedName result = QualifiedName.Parse(name);
            QualifiedName result2 = new QualifiedName(addName(result.Prefix), addName(result.LocalName));
            return result2;
        }

        private string addName(string name)
        {
            Contract.Requires(name == null || !name.Contains(":"));
            if (name == null)
                return null;
            return _nametable.Add(name);
        }

        private bool parseStartTag(char ch)
        {
            QualifiedName name;
            if (_state != SgmlReaderState.PseudoStartTag)
            {
                if (_tagterm.IndexOf(ch) >= 0)
                {
                    _sb.Length = 0;
                    //\\_sb = new StringBuilder();
                    _sb.Append('<');
                    _state = SgmlReaderState.PartialText;
                    return false;
                }
                name = scanName(_tagterm);
            }
            else
            {
                name = QualifiedName.Parse(startTag);
                _state = SgmlReaderState.Markup;
            }
            Node n = push(name, XmlNodeType.Element, null);
            n._isEmpty = false;
            ch = _current.SkipWhitespace();
            while (ch != Entity.Eof && ch != '>')
            {
                if (ch == '/')
                {
                    n._isEmpty = true;
                    ch = _current.ReadChar();
                    if (ch != '>')
                    {
                        _current.ScanToEnd(null, "Recovering", ">");
                        return false;
                    }
                    break;
                }
                if (ch == '<')
                {
                    break;
                }
                var aname = scanName(aterm);
                ch = _current.SkipWhitespace();
                string value = null;
                char quote = '\0';
                if (ch == '=' || ch == '"' || ch == '\'')
                {
                    if (ch == '=')
                    {
                        _current.ReadChar();
                        ch = _current.SkipWhitespace();
                    }
                    if (ch == '\'' || ch == '\"')
                    {
                        quote = ch;
                        value = ScanLiteral(_sb, ch);
                    }
                    else if (ch != '>')
                    {
                        const string term = avterm;
                        value = _current.ScanToken(_sb, term, false);
                    }
                }
                if (aname.LocalName.Length > 0)
                {
                    Attribute a = n.AddAttribute(aname, value, quote, folding == CaseFolding.None);
                }
                ch = _current.SkipWhitespace();
            }
            if (ch == Entity.Eof)
            {
                throw new InvalidOperationException();
            }
            else if (ch == '>')
            {
                _current.ReadChar(); 
            }
            if (Depth == 1)
            {
                if (_rootCount == 1)
                {
                    _state = SgmlReaderState.EndOfFile;
                    return false;
                }
                _rootCount++;
            }
            return true;
        }

        private bool parseEndTag()
        {
            _state = SgmlReaderState.EndTag;
            _current.ReadChar(); 
            QualifiedName name = scanName(_tagterm);
            char ch = _current.SkipWhitespace();
            if (ch != '>')
            {
                _current.ScanToEnd(null, "Recovering", ">");
            }
            _current.ReadChar(); 

            _endTag = name;
            
            const bool caseInsensitive = (folding == CaseFolding.None);
            Node = (Node) _stack[_stack.Count - 1];
            for (int i = _stack.Count - 1; i > 0; i--)
            {
                var n = _stack[i];
                if (caseInsensitive && n.Name.Equals(name, true))
                {
                    _endTag = n.LocalName;
                    return true;
                }
                if (n.Name.Equals(name))
                {
                    return true;
                }
            }
            _state = SgmlReaderState.Markup;
            return false;
        }

        private bool parseAspNet()
        {
            string value = "<%" + _current.ScanToEnd(_sb, "AspNet", "%>") + "%>";
            push(QualifiedName.Empty, XmlNodeType.CDATA, value);
            return true;
        }

        private bool parseComment()
        {
            char ch = _current.ReadChar();
            if (ch != '-')
            {
                _current.ScanToEnd(null, "Comment", ">");
                return false;
            }
            string value = _current.ScanToEnd(_sb, "Comment", "-->");

            int i = value.IndexOf("--");
            while (i >= 0)
            {
                int j = i + 2;
                while (j < value.Length && value[j] == '-')
                    j++;
                if (i > 0)
                {
                    value = value.Substring(0, i - 1) + "-" + value.Substring(j);
                }
                else
                {
                    value = "-" + value.Substring(j);
                }
                i = value.IndexOf("--");
            }
            if (value.Length > 0 && value[value.Length - 1] == '-')
            {
                value += " "; 
            }
            push(QualifiedName.Empty, XmlNodeType.Comment, value);
            return true;
        }

        private bool parseConditionalBlock()
        {
            char ch = _current.ReadChar(); 
            ch = _current.SkipWhitespace();
            string name = _current.ScanToken(_sb, cdataterm, false);
            if (name != "CDATA")
            {
                _current.ScanToEnd(null, "CDATA", ">");
                return false;
            }
            ch = _current.SkipWhitespace();
            if (ch != '[')
            {
#if SGML
                Debug.WriteLine(string.Format("Expecting '[' but found '{0}'", ch));
#endif
                _current.ScanToEnd(null, "CDATA", ">");
                return false;
            }
            string value = _current.ScanToEnd(_sb, "CDATA", "]]>");

            push(QualifiedName.Empty, XmlNodeType.CDATA, value);
            return true;
        }

        private void parseDocType()
        {
            char ch = _current.SkipWhitespace();
            var name = scanName(dtterm);
            push(name, XmlNodeType.DocumentType, null);
            ch = _current.SkipWhitespace();
            if (ch != '>')
            {
                string subset = "";
                string pubid = "";
                string syslit = "";

                if (ch != '[')
                {
                    string token = _current.ScanToken(_sb, dtterm, false);
                    if (token == "PUBLIC")
                    {
                        ch = _current.SkipWhitespace();
                        if (ch == '\"' || ch == '\'')
                        {
                            pubid = _current.ScanLiteral(_sb, ch);
                            Node.AddAttribute(QualifiedName.Parse(token), pubid, ch, folding == CaseFolding.None);
                        }
                    }
                    else if (token != "SYSTEM")
                    {
                        _current.ScanToEnd(null, "DOCTYPE", ">");
                    }
                    ch = _current.SkipWhitespace();
                    if (ch == '\"' || ch == '\'')
                    {
                        token = addName("SYSTEM");
                        syslit = _current.ScanLiteral(_sb, ch);
                        Node.AddAttribute(QualifiedName.Parse(token), syslit, ch, folding == CaseFolding.None);
                    }
                    ch = _current.SkipWhitespace();
                }
                if (ch == '[')
                {
                    subset = _current.ScanToEnd(_sb, "Internal Subset", "]");
                    Node.Value = subset;
                }
                ch = _current.SkipWhitespace();
                if (ch != '>')
                {
                    _current.ScanToEnd(null, "DOCTYPE", ">");
                }
            }
            _current.ReadChar();
        }

        private bool parsePI()
        {
            string name = _current.ScanToken(_sb, piTerm, false);
            string value;
            if (_current.Lastchar != '?')
            {
                value = _current.ScanToEnd(_sb, "Processing Instruction", ">");
            }
            else
            {
                value = _current.ScanToEnd(_sb, "Processing Instruction", ">");
            }
            if (name != "xml")
            {
                push(QualifiedName.Parse(addName(name)), XmlNodeType.ProcessingInstruction, value);
                return true;
            }
            return false;
        }

        private bool parseText(char ch, bool newtext)
        {
            bool ws = !newtext || _current.IsWhitespace;
            if (newtext)
            {
                _sb.Length = 0;
            }
            _state = SgmlReaderState.Text;
            while (ch != Entity.Eof)
            {
                if (ch == '<')
                {
                    ch = _current.ReadChar();
                    if (ch == '/' || ch == '!' || ch == '?' || Char.IsLetter(ch))
                    {
                        _state = SgmlReaderState.PartialTag;
                        _partial = ch;
                        break;
                    }
                    else
                    {
                        _sb.Append('<');
                        _sb.Append(ch);
                        ws = false;
                        ch = _current.ReadChar();
                    }
                }
                else if (ch == '&')
                {
                    expandEntity(_sb, '<');
                    ws = false;
                    ch = _current.Lastchar;
                }
                else
                {
                    if (!_current.IsWhitespace) ws = false;
                    _sb.Append(ch);
                    ch = _current.ReadChar();
                }
            }
            string value = _sb.ToString();
            push(QualifiedName.Empty, XmlNodeType.Text, value);
            return ws;
        }

        private string ScanLiteral(StringBuilder sb, char quote)
        {
            sb.Length = 0;
            char ch = _current.ReadChar();
            while (ch != Entity.Eof && ch != quote)
            {
                if (ch == '&')
                {
                    expandEntity(_sb, quote);
                    ch = _current.Lastchar;
                }
                else
                {
                    sb.Append(ch);
                    ch = _current.ReadChar();
                }
            }
            _current.ReadChar(); 
            return sb.ToString();
        }

        private bool parseCData()
        {
            bool ws = _current.IsWhitespace;
            _sb.Length = 0;
            
            char ch = _current.Lastchar;
            if (_partial != '\0')
            {
                pop(); 
                switch (_partial)
                {
                    case '!':
                        _partial = ' '; 
                        return parseComment();
                    case '?':
                        _partial = ' '; 
                        return parsePI();
                    case '/':
                        _state = SgmlReaderState.EndTag;
                        return true; 
                    case ' ':
                        break; 
                }
            }
            else
            {
                ch = _current.ReadChar();
            }

            while (ch != Entity.Eof)
            {
                if (ch == '<')
                {
                    ch = _current.ReadChar();
                    if (ch == '!')
                    {
                        ch = _current.ReadChar();
                        if (ch == '-')
                        {
                            if (ws)
                            {
                                _partial = ' '; 
                                return parseComment();
                            }
                            else
                            {
                                _partial = '!';
                                break;
                            }
                        }
                        else
                        {
                            _sb.Append('<');
                            _sb.Append('!');
                            _sb.Append(ch);
                            ws = false;
                        }
                    }
                    else if (ch == '?')
                    {
                        _current.ReadChar(); 
                        if (ws)
                        {
                            _partial = ' '; 
                            return parsePI();
                        }
                        else
                        {
                            _partial = '?';
                            break;
                        }
                    }
                    else if (ch == '/')
                    {
                       
                        string temp = _sb.ToString();
                        if (parseEndTag() && _endTag == (object) Node.LocalName)
                        {
                            if (ws || temp == "")
                            {
                                
                                return true;
                            }
                            else
                            {
                                
                                _partial = '/';
                                _sb.Length = 0; 
                                _sb = new StringBuilder();

                                _sb.Append(temp);
                                _state = SgmlReaderState.CData;
                                break;
                            }
                        }
                        else
                        {
                            
                            _sb.Length = 0; 
                            _sb = new StringBuilder();

                            _sb.Append(temp);
                            _sb.Append("</" + _endTag + ">");
                            ws = false;
                        }
                    }
                    else
                    {
                        
                        _sb.Append('<');
                        _sb.Append(ch);
                        ws = false;
                    }
                }
                else
                {
                    if (!_current.IsWhitespace && ws) ws = false;
                    _sb.Append(ch);
                }
                ch = _current.ReadChar();
            }
            string value = _sb.ToString();
            push(QualifiedName.Empty, XmlNodeType.CDATA, value);
            if (_partial == '\0')
                _partial = ' '; 
            return true;
        }

        private void expandEntity(StringBuilder sb, char terminator)
        {
            char ch = _current.ReadChar();
            if (ch == '#')
            {
                string charent = _current.ExpandCharEntity();
                sb.Append(charent);
                ch = _current.Lastchar;
            }
            else
            {
                _name.Length = 0;
                while (ch != Entity.Eof &&
                       (Char.IsLetter(ch) || ch == '_' || ch == '-'))
                {
                    _name.Append(ch);
                    ch = _current.ReadChar();
                }
                string name = _name.ToString();
                
                
                sb.Append("&");
                sb.Append(name);
                if (ch != terminator)
                {
                    sb.Append(ch);
                    ch = _current.ReadChar();
                }
            }
        }

        public override void Close()
        {
            if (_current != null)
            {
                _current.Close();
                _current = null;
            }
        }

        /// <summary>
        /// Reads the contents of an element or text node as a string.
        /// </summary>
        /// <returns>
        /// The contents of the element or an empty string.
        /// </returns>
        /// <exception cref="T:System.Xml.XmlException">An error occurred while parsing the XML. </exception>
        public override string ReadString()
        {
            if (Node._nodeType == XmlNodeType.Element)
            {
                var sb = new StringBuilder();

                while (Read())
                {
                    switch (NodeType)
                    {
                        case XmlNodeType.CDATA:
                        case XmlNodeType.SignificantWhitespace:
                        case XmlNodeType.Whitespace:
                        case XmlNodeType.Text:
                            sb.Append(Node.Value);
                            break;
                        default:
                            return sb.ToString();
                    }
                }
                return sb.ToString();
            }
            return Node.Value;
        }


        /// <summary>
        /// Reads all the content, including markup, as a string.
        /// </summary>
        /// <returns>
        /// All the XML content, including markup, in the current node. If the current node has no children, an empty string is returned.If the current node is neither an element nor attribute, an empty string is returned.
        /// </returns>
        /// <exception cref="T:System.Xml.XmlException">The XML was not well-formed, or an error occurred while parsing the XML. </exception>
        public override string ReadInnerXml()
        {
            var sw = new StringWriter();
            var xw = new XmlTextWriter(sw) {Formatting = Formatting.Indented};
            switch (NodeType)
            {
                case XmlNodeType.Element:
                    Read();
                    while (!EOF && NodeType != XmlNodeType.EndElement)
                    {
                        xw.WriteNode(this, true);
                    }
                    Read(); 
                    break;
                case XmlNodeType.Attribute:
                    sw.Write(Value);
                    break;
                default:
                    
                    break;
            }
            xw.Close();
            return sw.ToString();
        }

        /// <summary>
        /// Reads the content, including markup, representing this node and all its children.
        /// </summary>
        /// <returns>
        /// If the reader is positioned on an element or an attribute node, this method returns all the XML content, including markup, of the current node and all its children; otherwise, it returns an empty string.
        /// </returns>
        /// <exception cref="T:System.Xml.XmlException">The XML was not well-formed, or an error occurred while parsing the XML. </exception>
        public override string ReadOuterXml()
        {
            var sw = new StringWriter();
            var xw = new XmlTextWriter(sw) {Formatting = Formatting.Indented};
            xw.WriteNode(this, true);
            xw.Close();
            return sw.ToString();
        }

        /// <summary>
        /// Resolves a namespace prefix in the current element's scope.
        /// </summary>
        /// <returns>
        /// The namespace URI to which the prefix maps or null if no matching prefix is found.
        /// </returns>
        /// <param name="prefix">The prefix whose namespace URI you want to resolve. To match the default namespace, pass an empty string. </param>
        public override string LookupNamespace(string prefix)
        {
            return null;
        }

        /// <summary>
        /// Resolves the entity reference for EntityReference nodes.
        /// </summary>
        /// <exception cref="T:System.InvalidOperationException">The reader is not positioned on an EntityReference node; this implementation of the reader cannot resolve entities (<see cref="P:System.Xml.XmlReader.CanResolveEntity"/> returns false). </exception>
        public override void ResolveEntity()
        {
            throw new InvalidOperationException();
        }

        /// <summary>
        /// Parses the attribute value into one or more Text, EntityReference, or EndEntity nodes.
        /// </summary>
        /// <returns>
        /// true if there are nodes to return.false if the reader is not positioned on an attribute node when the initial call is made or if all the attribute values have been read.An empty attribute, such as, misc="", returns true with a single node with a value of String.Empty.
        /// </returns>
        public override bool ReadAttributeValue()
        {
            if (_state == SgmlReaderState.Attribute)
            {
                _state = SgmlReaderState.AttributeValue;
                return true;
            }
            else if (_state == SgmlReaderState.AttributeValue)
            {
                return false;
            }
            throw new InvalidOperationException();
        }
    }
}